package httpproxy.client;

import httpproxy.HttpProxyMain;
import httpproxy.server.DeniedItem;
import httpproxy.util.IProxyClientThreadListener;

import java.net.InetAddress;
import java.net.Socket;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
//import java.io.B

import javax.swing.event.EventListenerList;

public class ServerThread extends Thread 
{
	private EventListenerList evenLstList = new EventListenerList();;
	
	private static final int MAX_BUFFER_LENGTH = 1024;
	private Socket clientSocket;
	private InputStream clientInputStream;
	private OutputStream clientOutputStream;
	
	private boolean _isChainProxy = false;
	private String _chainProxyHost = "";
	private InetAddress _chainProxyAddress;
	private int _chainProxyPort = 0;
	
	public ServerThread()
	{
	}
	
	public ServerThread(Socket sk)
	{
		clientSocket = sk;
		try{
			clientInputStream = clientSocket.getInputStream();
			clientOutputStream = clientSocket.getOutputStream();
			//executer.start();
		}catch(IOException ex)
		{
			//UnsupportedEncodingException
			ex.printStackTrace();
		}
	}
	
	public ServerThread(Socket sk, String chainproxyHost, int chainPort)
	{
		clientSocket = sk;
		_isChainProxy = true;
		_chainProxyHost = chainproxyHost;
		_chainProxyPort = chainPort;
		
		try{
			
			Pattern chain_host_pattern = Pattern.compile("([0-9]{1,3}[\\.]){3}[0-9]{1,3}");
			Matcher chain_host_matcher = chain_host_pattern.matcher(_chainProxyHost);
			_chainProxyAddress = InetAddress.getByName(_chainProxyHost);
			if(chain_host_matcher.find())
			{
				String[] sb = _chainProxyHost.split("\\.");
				//System.out.println(_chainProxyHost + " - " + sb.length);
				byte[] addr = new byte[]{(byte)Integer.parseInt(sb[0]), (byte)Integer.parseInt(sb[1]), (byte)Integer.parseInt(sb[2]), (byte)Integer.parseInt(sb[3])};
				_chainProxyAddress = InetAddress.getByAddress(addr);
			}
			
			clientInputStream = clientSocket.getInputStream();
			clientOutputStream = clientSocket.getOutputStream();
			
			//executer.start();
			
		}catch(IOException ex)
		{
			ex.printStackTrace();
		}
	}
	
	public void addProxyClientListener(IProxyClientThreadListener lster)
	{
		evenLstList.add(IProxyClientThreadListener.class, lster);
	}
	
	public void removeProxyClientListener(IProxyClientThreadListener lster)
	{
		evenLstList.remove(IProxyClientThreadListener.class, lster);
	}
	
	private void fireThreadDone()
	{
		System.out.println("[ProxyClientThread] Chuan bi dong Client socket.");
		
		Object[] listeners = evenLstList.getListenerList();
	    // loop through each listener and pass on the event if needed
	    int numListeners = listeners.length;
	    for (int i = 0; i<numListeners - 1; i+=2)
	    {
	    	if (listeners[i] == IProxyClientThreadListener.class) 
	        {
	    		// pass the event to the listeners event dispatch method
	        	((IProxyClientThreadListener)listeners[i+1]).onClientDone(clientSocket);
	        }           
	    }
	}
	
	@Override
	public void run()
	{
		byte[] buff = new byte[MAX_BUFFER_LENGTH];
		int len = 0, requestCount = 0;
		
		HttpRequest request = null;
		
		if(_isChainProxy)
			request = new HttpRequest(String.format("%d - %d", Thread.currentThread().getId(), requestCount++), _chainProxyAddress, _chainProxyPort);
		else
			request = new HttpRequest(String.format("%d - %d", Thread.currentThread().getId(), requestCount++));
		
		int count;
		try{
			request.parse(clientInputStream);
			System.out.println("[ServerThread] Request method " + request.getMethod());
			//Gui request header den server
			//System.out.println("**************BEGIN SEND REQUEST***********");
			request.sendRequestHeaderString();
			
			//Neu la method POST thi gui them POST data
			if(request.getMethod().equals("POST"))
			{
				byte[] content = new byte[request.getContentLength()];
				
				if ( (count=clientInputStream.read(content)) > -1) 
				{	
					//System.out.print(new String(content, 0, count));
					request.getServerOutputStream().write(content, 0, count);    
		        }
				request.getServerOutputStream().flush();
			}
			//System.out.println("\n**************END SEND REQUEST***************");
			
			//Nhan ve response header tu server
			len = 0;
		    byte[] oneByte = new byte[1];
		       /*
		        * Read by lines
		        */
		       String[] lines = null;
		       String s = "";
		       try {
		    	   
		    	   while(request.getServerInputStream().read(oneByte) > -1)
		    	   {
		    		   buff[len++] = oneByte[0];
		    		   if(len>=4)
		    			   s = new String(buff, len-4, 4);
		    	
		    		   
		    		   if( len>=4 && s.contains("\r\n\r\n") )
		    		   {
		    			   String headers = new String(buff, 0, len);
		    			   //System.out.println("************RESPONSE HEADER FROM SERVER**************");
		    			   //System.out.print(headers);
		    			   //System.out.println("\n************RESPONSE HEADER FROM SERVER**************");
		    			   lines = ((headers.split("\r\n\r\n"))[0]).split("\r\n");
		    			   break;
		    		   }
		    	   }
		       } catch (IOException e) {
		           e.printStackTrace();
		       }
		       
		       int contentlength = 0;
		       if(lines != null) 
		       {
			    	System.out.println("************** RESPONSE HEADER AFTER PARSING ***********");
			    	DataOutputStream dout = new DataOutputStream(clientOutputStream);
			    	for(String line:lines)
			    	{
			    		//close
			    		//if(line.contains("Connection") && line.toLowerCase().contains("close"))
			    			//line = line.replace("close", "keep-alive");
			    		if(line.contains("Connection") && line.toLowerCase().contains("keep-alive"))
			    			line = line.replace("keep-alive", "close");
			    		if(line.contains("Content-Length"))
			    			contentlength = Integer.parseInt(line.replace("Content-Length:", "").trim());
			    		
			    		//Access control check
			    		if(line.toUpperCase().startsWith("CONTENT-TYPE"))
			    		{
			    			String server = request.getHost();
			    			String type = line.replace("Content-Type:", "").trim();
			    			for(DeniedItem itm:HttpProxyMain.getACL())
			    			{
			    				if(itm.server.equals(server) && itm.type.endsWith(type))
			    				{
			    					
			    					break;
			    				}
			    			}
			    		}
			    		
			    		//Gui header ve client
			    		dout.writeBytes(line + "\r\n");
			    		System.out.print(line + "\r\n");
			    	}
			    	dout.writeBytes("\r\n");
			    	dout.flush();
			    	System.out.println("\r\n**************RESPONSE HEADER AFTER PARSING***************");
		       }
		       System.out.println("\r\nRESPONSE " + contentlength + "\r\n");
		       
		    //Doc response data tu server va gui ve phia client
		    InputStream  in  = request.getServerInputStream();
			int byteread = 0;
			while (-1 < ( count  = in.read(buff))) 
			{
				clientOutputStream.write(buff,0,count);
				clientOutputStream.flush();
				byteread += count;
				
				Thread.yield();
				
				if(byteread == contentlength)
					break;
			}
			
			//Dong ket noi den server va client
			try{
				request.getWebServerSocket().close();
				clientSocket.close();
			}catch(IOException ex)
			{
				ex.printStackTrace();
			}
				
			return;
		}catch(IOException ex)
		{
			ex.printStackTrace();
		}
	}
}